package tech

import (
	"slices"
)

func (s *Solve) Suedecoq(g *Grid) (used bool, results []*Result) {
	for boxNum, box := range g.boxes {
		rowStart := boxNum / 3 * 3
		colStart := boxNum % 3 * 3
		for _, row := range g.rows[rowStart : rowStart+3] {
			cells := row.cells[colStart : colStart+3]
			if ok, rs := g.suedecoqHouses(cells, box, row); ok {
				used = true
				results = append(results, rs...)
			}
		}
		for _, col := range g.cols[colStart : colStart+3] {
			cells := col.cells[rowStart : rowStart+3]
			if ok, rs := g.suedecoqHouses(cells, box, col); ok {
				used = true
				results = append(results, rs...)
			}
		}
	}
	return
}

func (g *Grid) suedecoqHouses(cells []*Cell, box House, line House) (used bool, results []*Result) {
	cells = getUnsolvedCells(cells)
	n := len(cells)
	if n == 2 {
		return g.suedecoqHouses2(cells, box, line)
	} else if n == 3 {
		for _, cs := range combine(cells, 2) {
			if ok, rs := g.suedecoqHouses2(cs, box, line); ok {
				used = true
				results = append(results, rs...)
			}
		}
		if ok, rs := g.suedecoqHouses3(cells, box, line); ok {
			used = true
			results = append(results, rs...)
		}

	}
	return
}
func (g *Grid) suedecoqHouses2(shareCells []*Cell, box House, line House) (ok bool, results []*Result) {
	n := len(shareCells)
	shareCands := unionCellCands(shareCells)
	m := shareCands.Count()
	if m < n+2 {
		return
	}
	boxCells := []*Cell{}
	for _, cell := range box.cells {
		if !cell.solved() && !slices.Contains(shareCells, cell) {
			boxCells = append(boxCells, cell)
		}
	}
	lineCells := []*Cell{}
	for _, cell := range line.cells {
		if !cell.solved() && !slices.Contains(shareCells, cell) {
			lineCells = append(lineCells, cell)
		}
	}

	for i := 1; i < len(boxCells); i++ {
		for _, bcells := range combine(boxCells, i) {
			bcands := unionCellCands(bcells)
			if bcands.Count() != len(bcells)+1 {
				continue
			}
			clearbcands := bcands.Intersect(shareCands)
			if clearbcands.Count() < 2 {
				continue
			}
			for j := 1; j < len(lineCells); j++ {
				if i+j+n < m {
					continue
				}
				for _, lcells := range combine(lineCells, j) {
					lcands := unionCellCands(lcells)
					if lcands.Count() != len(lcells)+1 {
						continue
					}
					clearlcands := lcands.Intersect(shareCands)
					if clearlcands.Count() < 2 {
						continue
					}
					if !clearbcands.Intersect(clearlcands).IsNull() {
						continue
					}
					if clearbcands.Union(clearlcands) != shareCands {
						continue
					}

					//fmt.Println(i, j, bcells, lcells, shareCells)
					result := new(Result)
					for _, cell := range boxCells {
						if slices.Contains(bcells, cell) {
							continue
						}
						if cell.canRemoveCands(bcands) {
							result.addRemoved(bcands, []*Cell{cell})
							ok = true
						}
					}
					for _, cell := range lineCells {
						if slices.Contains(lcells, cell) {
							continue
						}
						if cell.canRemoveCands(lcands) {
							result.addRemoved(lcands, []*Cell{cell})
							ok = true
						}
					}
					if !ok {
						continue
					}
					g1 := result.getHelpCandsGroup()
					g1.add(bcands, bcells)
					g1.add(clearbcands, shareCells)
					g2 := result.getHelpCandsGroup()
					g2.add(lcands, lcells)
					g2.add(clearlcands, shareCells)
					result.addHelpCells(shareCells)
					result.addHelpCells(bcells)
					result.addHelpCells(lcells)
					result.setStrategyName("Sue de Coq")
					results = append(results, result)
					return
				}
			}
		}
	}

	return
}

func (g *Grid) suedecoqHouses3(shareCells []*Cell, box House, line House) (ok bool, results []*Result) {
	n := len(shareCells)
	shareCands := unionCellCands(shareCells)
	m := shareCands.Count()
	if m < n+2 {
		return
	}
	boxCells := []*Cell{}
	for _, cell := range box.cells {
		if !cell.solved() && !slices.Contains(shareCells, cell) {
			boxCells = append(boxCells, cell)
		}
	}
	lineCells := []*Cell{}
	for _, cell := range line.cells {
		if !cell.solved() && !slices.Contains(shareCells, cell) {
			lineCells = append(lineCells, cell)
		}
	}

	for i := 1; i < len(boxCells); i++ {
		for _, bcells := range combine(boxCells, i) {
			bcands := unionCellCands(bcells)
			clearbcands := bcands.Intersect(shareCands)
			baals := false
			switch bcands.Count() - len(bcells) {
			case 1:
				if clearbcands.Count() < 2 {
					continue
				}
			case 2:
				baals = true
				if clearbcands.Count() < 3 {
					continue
				}
			default:
				continue
			}
			for j := 1; j < len(lineCells); j++ {
				if i+j+n < m {
					continue
				}
				for _, lcells := range combine(lineCells, j) {
					lcands := unionCellCands(lcells)
					clearlcands := lcands.Intersect(shareCands)
					laals := false
					switch lcands.Count() - len(lcells) {
					case 1:
						if clearlcands.Count() < 2 {
							continue
						}
					case 2:
						if baals {
							continue
						}
						laals = true
						if clearlcands.Count() < 3 {
							continue
						}
					default:
						continue
					}
					if !clearbcands.Intersect(clearlcands).IsNull() {
						continue
					}
					z := shareCands.Diff(clearbcands.Union(clearlcands))
					if (!baals && !laals && z.Count() != 1) || ((baals || laals) && z.Count() != 0) {
						continue
					}
					result := new(Result)
					for _, cell := range boxCells {
						if slices.Contains(bcells, cell) {
							continue
						}
						if cell.canRemoveCands(bcands.Union(z)) {
							result.addRemoved(bcands.Union(z), []*Cell{cell})
							ok = true
						}
					}
					for _, cell := range lineCells {
						if slices.Contains(lcells, cell) {
							continue
						}
						if cell.canRemoveCands(lcands.Union(z)) {
							result.addRemoved(lcands.Union(z), []*Cell{cell})
							ok = true
						}
					}
					if !ok {
						continue
					}
					g1 := result.getHelpCandsGroup()
					g1.add(bcands, bcells)
					g1.add(clearbcands, shareCells)
					g2 := result.getHelpCandsGroup()
					g2.add(lcands, lcells)
					g2.add(clearlcands, shareCells)
					if !z.IsNull() {
						g3 := result.getHelpCandsGroup()
						g3.add(z, shareCells)
					}
					result.addHelpCells(shareCells)
					result.addHelpCells(bcells)
					result.addHelpCells(lcells)
					result.setStrategyName("Sue de Coq")
					results = append(results, result)
					return
				}
			}
		}
	}

	return
}

/*
func (g *Grid) suedecoqHouses(cells []*Cell, box House, line House) (ok bool, results []*Result) {
	cells = getUnsolvedCells(cells)
	if len(cells) != 2 {
		return
	}
	if len(cells) < 2 {
		return
	}
	coms := combine(cells, 2)
	if len(cells) == 3 {
		coms = append(coms, cells)
	}
	for _, shareCells := range coms {
		n := len(shareCells)
		shareCands := unionCellCands(shareCells)
		m := shareCands.Count()
		if m < n+2 {
			continue
		}
		boxCells := []*Cell{}
		for _, cell := range box.cells {
			if !cell.solved() && !slices.Contains(shareCells, cell) {
				boxCells = append(boxCells, cell)
			}
		}
		lineCells := []*Cell{}
		for _, cell := range line.cells {
			if !cell.solved() && !slices.Contains(shareCells, cell) {
				lineCells = append(lineCells, cell)
			}
		}
		for i := 1; i < len(boxCells); i++ {
			for _, bcells := range combine(boxCells, i) {
				bcands := unionCellCands(bcells)
				if bcands.Count() < len(bcells)+1 || bcands.Count() > len(bcells)+n-1 {
					continue
				}
				for j := 1; j < len(lineCells); j++ {
					if i+j+n < m {
						continue
					}
					for _, lcells := range combine(lineCells, j) {
						lcands := unionCellCands(lcells)
						if lcands.Count() != len(lcells)+1 {
							continue
						}

					}
				}
			}
		}

	}
	return
}
*/
